/*
 Erica Sadun, http://ericasadun.com
 iPhone Developer's Cookbook, 3.0 Edition
 BSD License, Use at your own risk
 */

#import "ABContactsHelper.h"
#import "NSError+CFErrorRef.h"

#define CFAutorelease(obj) ({CFTypeRef _obj = (obj); (_obj == NULL) ? NULL : [(id)CFMakeCollectable(_obj) autorelease]; })

@implementation ABContactsHelper


/*
 Note: You cannot CFRelease the addressbook after CFAutorelease(ABAddressBookCreate());
 */
+ (ABAddressBookRef)addressBook {
    
    static dispatch_once_t predicate;
    static ABAddressBookRef sharedABAddressBook = NULL;
    dispatch_once(&predicate, ^{
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_6_0
        sharedABAddressBook = ABAddressBookCreateWithOptions(NULL, NULL);
#else
        sharedABAddressBook = ABAddressBookCreate();
#endif
    });
    
    return sharedABAddressBook;
}

#if (!__has_feature(objc_arc))

- (id)retain {
    
    return self;
}

- (unsigned)retainCount {
    return UINT_MAX;  //denotes an object that cannot be released
}

- (oneway void)release {
    //do nothing
}

- (id)autorelease {
    
    return self;
}
#endif

+ (int)contactsCount {
	ABAddressBookRef addressBook = [ABContactsHelper addressBook];
	return ABAddressBookGetPersonCount(addressBook);
}

+ (int)contactsWithImageCount {
    int ncount = 0;
	ABAddressBookRef addressBook = [ABContactsHelper addressBook];
	CFArrayRef peopleArray = ABAddressBookCopyArrayOfAllPeople(addressBook);
    CFIndex count = CFArrayGetCount(peopleArray);
	for (int i = 0; i < count; i++) {
        ABRecordRef person = CFArrayGetValueAtIndex(peopleArray, i);
        if (ABPersonHasImageData(person)) {
            ncount++;
        }
    }
    CFRelease(peopleArray);
	return ncount;
}

+ (int)contactsWithoutImageCount {
    int ncount = 0;
	ABAddressBookRef addressBook = [ABContactsHelper addressBook];
	CFArrayRef peopleArray = ABAddressBookCopyArrayOfAllPeople(addressBook);
    CFIndex count = CFArrayGetCount(peopleArray);
	for (int i = 0; i < count; i++) {
        ABRecordRef person = CFArrayGetValueAtIndex(peopleArray, i);
        if (!ABPersonHasImageData(person)) {
            ncount++;
        }
    }
    CFRelease(peopleArray);
	return ncount;
}

// Groups
+ (int)numberOfGroups {
	ABAddressBookRef addressBook = [ABContactsHelper addressBook];
	CFArrayRef groups = ABAddressBookCopyArrayOfAllGroups(addressBook);
	int ncount = CFArrayGetCount(groups);
	CFRelease(groups);
	return ncount;
}

+ (NSArray *)groups {
	ABAddressBookRef addressBook = [ABContactsHelper addressBook];
	CFArrayRef groups = ABAddressBookCopyArrayOfAllGroups(addressBook);
	int ncount = CFArrayGetCount(groups);
    
	NSMutableArray *array = [NSMutableArray arrayWithCapacity:ncount];
	for (int i = 0; i < ncount; i++) {
        ABRecordRef group = CFArrayGetValueAtIndex(groups, i);
		[array addObject:[ABGroup groupWithRecord:group]];
    }
	CFRelease(groups);
	return array;
}

// Sorting
+ (BOOL)firstNameSorting {
    if(SupportiOS7) {
        return (ABPersonGetCompositeNameFormatForRecord(NULL) == kABPersonCompositeNameFormatFirstNameFirst);
    } else {
        return (ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatFirstNameFirst);
    }
}

#pragma mark Contact Management

// Thanks to Eridius for suggestions re: error
+ (BOOL)addContact:(ABContact *)aContact withError:(NSError **)error {
    CFErrorRef *cfError = (CFErrorRef *)(void *)error;
	ABAddressBookRef addressBook = [ABContactsHelper addressBook];
	if (!ABAddressBookAddRecord(addressBook, aContact.record, cfError)) return NO;
	return ABAddressBookSave(addressBook, cfError);
}

+ (BOOL)addGroup:(ABGroup *)aGroup withError:(NSError **)error {
    CFErrorRef *cfError = (CFErrorRef *)(void *)error;
	ABAddressBookRef addressBook = [ABContactsHelper addressBook];
	if (!ABAddressBookAddRecord(addressBook, aGroup.record, cfError)) return NO;
	return ABAddressBookSave(addressBook, cfError);
}

/* Using block to access contacts */
/* Khoai added iOS 6.0 support */
+ (void)getAccessPermissionUsingBlock:(void(^)(BOOL granted, ABAddressBookRef addressBook))block {
    NSString *currentVersion = [UIDevice currentDevice].systemVersion;
    
    if([currentVersion floatValue] > 6.0) {
        ABAddressBookRef addressBook = [ABContactsHelper addressBook];
        ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
            block(granted, addressBook);
        });
    } else {
        ABAddressBookRef addressBook = [ABContactsHelper addressBook];
        block(YES, addressBook);
    }
}

+ (void)getContactsUsingBlock:(void(^)(NSArray *contacts))block {
    [ABContactsHelper getAccessPermissionUsingBlock:^(BOOL granted, ABAddressBookRef addressBook) {
        if(granted) {
            CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
            CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);
            NSMutableArray *array = [NSMutableArray arrayWithCapacity:numberOfPeople];
            
            for(int i = 0; i < numberOfPeople; i++) {
                /* enumerate each address record */
                ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
                [array addObject:[ABContact contactWithRecord:person]];
            }
            CFRelease(allPeople);
            block(array);
        } else {
            block(nil);
        }
    }];
}

+ (void)getContactsMatchingName:(NSString *)fname
                     usingBlock:(void(^)(NSArray *result))block {
    
    [ABContactsHelper getContactsUsingBlock:^(NSArray *contacts) {
        if (contacts.count > 0) {
            NSPredicate *pred = [NSPredicate predicateWithFormat:@"firstname contains[cd] %@ OR lastname contains[cd] %@ OR nickname contains[cd] %@ OR middlename contains[cd] %@", fname, fname, fname, fname];
            block([contacts filteredArrayUsingPredicate:pred]);
        } else {
            block(nil);
        }
    }];
}

+ (void)getContactsMatchingName:(NSString *)fname
                        andName:(NSString *)lname
                     usingBlock:(void(^)(NSArray *result))block {
    
    [ABContactsHelper getContactsUsingBlock:^(NSArray *contacts) {
        if (contacts.count > 0) {
            NSPredicate *pred = [NSPredicate predicateWithFormat:@"firstname contains[cd] %@ OR lastname contains[cd] %@ OR nickname contains[cd] %@ OR middlename contains[cd] %@", fname, fname, fname, fname];
            NSArray *result = [contacts filteredArrayUsingPredicate:pred];
            
            pred = [NSPredicate predicateWithFormat:@"firstname contains[cd] %@ OR lastname contains[cd] %@ OR nickname contains[cd] %@ OR middlename contains[cd] %@", lname, lname, lname, lname];
            result = [result filteredArrayUsingPredicate:pred];
            
            block(result);
        } else {
            block(nil);
        }
    }];
}

+ (void)getContactsMatchingPhone:(NSString *)number
                      usingBlock:(void(^)(NSArray *result))block {
    
    [ABContactsHelper getContactsUsingBlock:^(NSArray *contacts) {
        if (contacts.count > 0) {
            NSPredicate *pred = pred = [NSPredicate predicateWithFormat:@"phonenumbers contains[cd] %@", number];
            NSArray *result = [contacts filteredArrayUsingPredicate:pred];
            block(result);
        } else {
            block(nil);
        }
    }];
}

+ (void)getGroupsMatchingName:(NSString *)fname
                   usingBlock:(void(^)(NSArray *result))block {
    
    [ABContactsHelper getContactsUsingBlock:^(NSArray *contacts) {
        if (contacts.count > 0) {
            NSPredicate *pred = [NSPredicate predicateWithFormat:@"name contains[cd] %@ ", fname];
            NSArray *result = [contacts filteredArrayUsingPredicate:pred];
            block(result);
        } else {
            block(nil);
        }
    }];
}

@end